package cn.wolfcode.service.impl;

import cn.wolfcode.common.domain.UserInfo;
import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.*;
import cn.wolfcode.feign.AliPayApi;
import cn.wolfcode.feign.IntergralPayApi;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.PayLogMapper;
import cn.wolfcode.mapper.RefundLogMapper;
import cn.wolfcode.mapper.SeckillProductMapper;
import cn.wolfcode.mq.MQConstant;
import cn.wolfcode.mq.OrderMQResult;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.UserUtil;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

/**
 * Created by wolfcode-lanxw
 */
@Service
public class OrderInfoSeviceImpl implements IOrderInfoService {
    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private PayLogMapper payLogMapper;
    @Autowired
    private RefundLogMapper refundLogMapper;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private AliPayApi aliPayApi;
    @Autowired
    private SeckillProductMapper seckillProductMapper;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Value("${pay.notifyUrl}")
    private String notify_url;
    @Value("${pay.returnUrl}")
    private String return_url;
    @Autowired
    private IntergralPayApi intergralPayApi;

    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        return orderInfoMapper.find(orderNo);
    }

    //修改订单状态
    public void updateStatus(String orderNo) {
        orderInfoMapper.updateCancelStatus(orderNo,OrderInfo.STATUS_TIMEOUT);
    }

    //查询出该订单
    public OrderInfo find(String token, String orderNo) {
//        OrderInfo orderInfo = orderInfoMapper.find(orderNo);
        //1、从缓存中根据订单号获取该订单
        Object orderStringJson = redisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_ORDER_HASH.getRealKey(""), orderNo);
        OrderInfo orderInfo = JSON.parseObject(orderStringJson.toString(), OrderInfo.class);
        //判断该订单是否属于该用户
        UserInfo user = UserUtil.getUser(redisTemplate, token);
        if(!user.getPhone().equals(orderInfo.getUserId())){
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_USERORDER);
        }
        return orderInfo;
    }

    //异步回调：在这里执行订单成功之后的业务逻辑
    public String notify_url(Map<String, String> params) {
        String out_trade_no = params.get("out_trade_no");
        //从redis查出对应的订单
        OrderInfo orderInfo =null;
        Object orderStringJsson = stringRedisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_ORDER_HASH.getRealKey(""), out_trade_no);
        if(orderStringJsson!=null){
            orderInfo = JSON.parseObject(orderStringJsson.toString(), OrderInfo.class);
        }
        //1、插入一条流水记录pay_log
        PayLog payLog = new PayLog();
        payLog.setTradeNo(out_trade_no);
        payLog.setTotalAmount(orderInfo.getProductPrice().toString());
        payLog.setPayType(orderInfo.getPayType());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String notifyTime = simpleDateFormat.format(new Date());
        payLog.setNotifyTime(notifyTime);//注意日期需要String类型
        payLogMapper.insert(payLog);
        //2、修改订单状态为已支付
        orderInfoMapper.changePayStatus(out_trade_no,OrderInfo.STATUS_ACCOUNT_PAID,orderInfo.getPayType());
        return "success";
    }

    //申请退款：
    public Result<Boolean> refundOrder(String orderNo) {
        Object orderStringJson = stringRedisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_ORDER_HASH.getRealKey(""), orderNo);
        OrderInfo orderInfo = JSON.parseObject(orderStringJson.toString(), OrderInfo.class);//先从redis获取订单数据
        //1、插入一条流水记录refund_log
        RefundLog refundLog = new RefundLog();
        refundLog.setOutTradeNo(orderNo);
        refundLog.setRefundAmount(orderInfo.getProductPrice().toString());
        refundLog.setRefundReason("garbage 不要了..");//这里默认把
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String refundTime = simpleDateFormat.format(new Date());
        refundLog.setRefundTime(refundTime);
        refundLog.setRefundType(orderInfo.getPayType());
//        refundLogMapper.insert(refundLog);
        //封装支付服务接口所需参数
        RefundVo refundVo = new RefundVo();
        refundVo.setOutTradeNo(orderNo);
        refundVo.setRefundAmount(orderInfo.getProductPrice().toString());
        refundVo.setRefundReason("garbage 不要了..");
        //2远程调用支付服务：退款接口
        Result<Boolean> result=aliPayApi.refund(refundVo);
        Boolean isRefundSuccess = result.getData();
        if(isRefundSuccess){//退款成功
            //3修改订单状态为退款状态
            orderInfoMapper.changeRefundStatus(orderNo,OrderInfo.STATUS_REFUND);
            //4、回补真实库存
            seckillProductMapper.incrStock(orderInfo.getSeckillId());
            //5、回补预库存
            seckillProductService.initPreCountInRedis(orderInfo.getSeckillTime(),orderInfo.getSeckillId());
            //6、修改本地标识
            OrderMQResult orderMQResult = new OrderMQResult();//封装OrderMQResult，只需要秒杀商品id
            orderMQResult.setOrderNo(orderNo);
            orderMQResult.setSeckillId(orderInfo.getSeckillId());
            rocketMQTemplate.sendOneWay(MQConstant.CANCEL_SECKILL_OVER_SIGE_TOPIC,orderMQResult);
        }

        return Result.success(isRefundSuccess);
    }

    //在线支付
    public Result<String> onlinePay(OrderInfo orderInfo) {
        // 1、远程调用支付宝支付接口。注意返回的String类型的表单数据
        PayVo payVo = new PayVo();//封装支付宝接口所需配置参数，其中回调函数必须由服务指定给出.
        payVo.setOutTradeNo(orderInfo.getOrderNo());
        payVo.setTotalAmount(orderInfo.getProductPrice().toString());
        payVo.setSubject(orderInfo.getProductName());//订单名称
        payVo.setBody("");//可以随意指定,也可以为null
        payVo.setNotifyUrl(notify_url);
        payVo.setReturnUrl(return_url);
        Result<String> result=aliPayApi.orderAlipay(payVo);
        //这里可以增加远程调用结果的判断，以增强程序健壮性
        /****/
        return result;
    }

    //同步回调的验签操作
    public Result<Boolean> rshcheck(Map<String, String> params) {
        String out_trade_no = params.get("out_trade_no");//订单号
        //1、从redis中查出该订单
        Object orderStringJson = stringRedisTemplate.opsForHash().get(SeckillRedisKey.SECKILL_ORDER_HASH.getRealKey(""), out_trade_no);
        OrderInfo orderInfo = JSON.parseObject(orderStringJson.toString(), OrderInfo.class);
        //2、验签操作：远程调用pay-service服务接口
        Result<Boolean> result=aliPayApi.rsaCheck(params);
        /**/
        return result;
    }

    //积分支付
    @Transactional
    public void integralPay(OrderInfo orderInfo) {
        //1、校验参数
        //2、插入一条支付流水记录
        PayLog payLog = new PayLog();
        payLog.setTradeNo(orderInfo.getOrderNo());
        payLog.setTotalAmount(orderInfo.getProductPrice().toString());
        payLog.setPayType(OrderInfo.PAYTYPE_INTERGRAL);//设置流水记录支付状态为积分支付
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String notifyTime = simpleDateFormat.format(new Date());
        payLog.setNotifyTime(notifyTime);
        payLogMapper.insert(payLog);
        //3、远程调用积分服务(当前用户)减积分
        OperateIntergralVo operateIntergralVo = new OperateIntergralVo();
        operateIntergralVo.setPk(orderInfo.getOrderNo());
        operateIntergralVo.setValue(orderInfo.getIntergral());
        operateIntergralVo.setUserId(orderInfo.getUserId());
        operateIntergralVo.setInfo("支付减积分..");//默认的备注
        Result<String> result=intergralPayApi.decrIntergral(operateIntergralVo);
//        int i=1/0;模拟异常，看能否全部回滚.
        //***如果调用积分失败则直接抛异常
        if(result==null || result.hasError()){//返回为空或者状态码不是200，抛异常
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);//操作积分失败
        }
        //4、修改订单状态(包括支付类型):为已支付，积分支付类型
        orderInfoMapper.changePayStatus(orderInfo.getOrderNo(),OrderInfo.STATUS_ACCOUNT_PAID,OrderInfo.PAYTYPE_INTERGRAL);
    }

    //积分退款
//    @Transactional
    @GlobalTransactional
    public Result<Boolean> intergralRefundOrder(OrderInfo orderInfo) {
        //**参数校验
        //1、插入退款流水记录
        RefundLog refundLog = new RefundLog();
        refundLog.setOutTradeNo(orderInfo.getOrderNo());
        refundLog.setRefundAmount(orderInfo.getIntergral().toString());
        refundLog.setRefundReason("garbage 不要了..");//这里默认把
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String refundTime = simpleDateFormat.format(new Date());
        refundLog.setRefundTime(refundTime);
        refundLog.setRefundType(orderInfo.getPayType());
        refundLogMapper.insert(refundLog);//利用mysql主键进行非幂等性问题的解决
        //2、远程调用积分服务加积分
        OperateIntergralVo operateIntergralVo = new OperateIntergralVo();//积分服务退款需要vo参数
        operateIntergralVo.setPk(orderInfo.getOrderNo());
        operateIntergralVo.setValue(orderInfo.getIntergral());
        operateIntergralVo.setUserId(orderInfo.getUserId());
        operateIntergralVo.setInfo("退款加积分！");//设置默认备注
        Result<Boolean> result=intergralPayApi.incrIntergral(operateIntergralVo);
//        int i=1/0;
        if(result==null || result.hasError()){
            throw new BusinessException(SeckillCodeMsg.INTERGRAL_SERVER_ERROR);//积分操作失败
        }
        //3、修改订单状态为退款状态
        orderInfoMapper.changeRefundStatus(orderInfo.getOrderNo(),OrderInfo.STATUS_REFUND);
        return result;
    }

}
